package com.cyj.dream.file.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.cyj.dream.core.constant.pagemodel.ResponseUtil;
import com.cyj.dream.core.util.date.DateUtils;
import com.cyj.dream.file.contacts.FileConstant;
import com.cyj.dream.file.mapper.FileUploadPieceRecordMapper;
import com.cyj.dream.file.mapper.FileUploadRecordMapper;
import com.cyj.dream.file.model.FileUploadPieceRecord;
import com.cyj.dream.file.model.FileUploadRecord;
import com.cyj.dream.file.service.FileManagementService;
import com.cyj.dream.file.util.MinIoUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;

/**
 * @Description: 文件管理实现类
 * @BelongsProject: DreamChardonnay
 * @BelongsPackage: com.cyj.dream.file.service.impl
 * @Author: ChenYongJia
 * @CreateTime: 2021-09-13 14:44
 * @Email: chen87647213@163.com
 * @Version: 1.0
 */
@Slf4j
@Service
public class FileManagementServiceImpl implements FileManagementService {

    /**
     * 上传成功
     */
    private final Integer UPLOAD_SUCCESS = 1;

    /**
     * 部分上传
     */
    private final Integer UPLOAD_PART = 0;

    @Resource
    private FileUploadRecordMapper fileUploadRecordMapper;

    @Resource
    private FileUploadPieceRecordMapper fileUploadPieceRecordMapper;

    @Override
    public Object initChunkUpload(String md5, String chunkCount, String fileSize) {
        try {
            FileUploadPieceRecord uploadDto = new FileUploadPieceRecord();
            uploadDto.setFileSize(fileSize);
            uploadDto.setFileMd5(md5);
            uploadDto.setChunkCount(Long.valueOf(chunkCount));
            QueryWrapper<FileUploadPieceRecord> queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda().eq(FileUploadPieceRecord::getFileMd5, md5);
            FileUploadPieceRecord mysqlFileData = fileUploadPieceRecordMapper.selectOne(queryWrapper);
            if (ObjectUtil.isNotEmpty(mysqlFileData)) {
                //秒传
                if (UPLOAD_SUCCESS.equals(mysqlFileData.getUploadStatus())) {
                    return mysqlFileData;
                }
                //续传
                //获取到该文件已上传分片
                Map<Integer, String> okChunkMap = MinIoUtils.mapChunkObjectNames(MinIoUtils.chunkBucKet, uploadDto.getFileMd5());
                List<FileUploadPieceRecord> chunkUploadUrls = new ArrayList<>();
                if (ObjectUtil.isNotEmpty(okChunkMap) && okChunkMap.size() > 0) {
                    for (int i = 1; i <= uploadDto.getChunkCount(); i++) {
                        //判断当前分片是否已经上传过了
                        if (!okChunkMap.containsKey(i)) {
                            //生成分片上传url
                            FileUploadPieceRecord url = new FileUploadPieceRecord();
                            url.setPartNumber(i);
                            url.setUploadUrl(MinIoUtils.createUploadChunkUrl(MinIoUtils.chunkBucKet, uploadDto.getFileMd5(), i));
                            chunkUploadUrls.add(url);
                        }
                    }
                    if (chunkUploadUrls.size() == 0) {
                        return "所有分片已经上传完成，仅需要合并文件";
                    }
                    return chunkUploadUrls;
                }
            }
            //初次上传和已有文件信息但未上传任何分片的情况下则直接生成所有上传url
            List<String> uploadUrls = MinIoUtils.createUploadChunkUrlList(MinIoUtils.chunkBucKet, uploadDto.getFileMd5(),
                    Integer.valueOf(uploadDto.getChunkCount().toString()));
            List<FileUploadPieceRecord> chunkUploadUrls = new ArrayList<>();
            for (int i = 1; i <= uploadUrls.size(); i++) {
                FileUploadPieceRecord url = new FileUploadPieceRecord();
                url.setPartNumber(i);
                url.setUploadUrl(uploadUrls.get(i - 1));
                chunkUploadUrls.add(url);
            }
            //向数据库中记录该文件的上传信息
            uploadDto.setUploadStatus(UPLOAD_PART);
            if (ObjectUtil.isEmpty(mysqlFileData)) {
                uploadDto.setChunkBucketName(MinIoUtils.chunkBucKet);
                uploadDto.setFileBucketName(MinIoUtils.originFileBucKet);
                uploadDto.setCreateTime(DateUtils.toLocalDateTime(new Date()));
                uploadDto.setUpdateTime(DateUtils.toLocalDateTime(new Date()));
                fileUploadPieceRecordMapper.insert(uploadDto);
            }
            return chunkUploadUrls;
        } catch (Exception ex) {
            log.error("发生异常，异常信息为：{}", ex);
            return ResponseUtil.error("初始化文件失败");
        }
    }

    @Override
    public Object composeFile(String md5, String fileName) {
        try {
            //根据md5获取所有分片文件名称(minio的文件名称 = 文件path)
            List<String> chunks = MinIoUtils.listObjectNames(MinIoUtils.chunkBucKet, md5);
            //获取过来的分片文件名称是乱序的，所以重新组合排序一下
            List<String> newChunks = new ArrayList<>(chunks.size());
            for (int i = 1; i <= chunks.size(); i++) {
                String newChunkName = md5 + "/" + i + ".chunk";
                newChunks.add(newChunkName);
            }
            //自定义文件名称
            String suffix = fileName.substring(fileName.lastIndexOf("."));
            String filePath = md5 + "/" + fileName;
            //合并文件
            if (!MinIoUtils.composeObject(newChunks, filePath)) {
                return ResponseUtil.error("合并文件失败");
            }
            String url = null;
            try {
                //获取文件访问外链(1小时过期)
                url = MinIoUtils.getOriginalObjectUrl(filePath, 60);
            } catch (Exception e) {
                log.error("发生异常，异常信息为：{}", e);
                return ResponseUtil.error("获取文件下载连接失败");
            }
            //获取数据库里记录的文件信息，修改数据并返回文件信息
            QueryWrapper<FileUploadPieceRecord> queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda().eq(FileUploadPieceRecord::getFileMd5, md5);
            FileUploadPieceRecord dbData = fileUploadPieceRecordMapper.selectOne(queryWrapper);
            dbData.setUploadStatus(UPLOAD_SUCCESS);
            dbData.setFileName(fileName);
            dbData.setFilePath(url);
            dbData.setFileSuffix(suffix);

            dbData.setCreateTime(DateUtils.toLocalDateTime(new Date()));
            dbData.setUpdateTime(DateUtils.toLocalDateTime(new Date()));
            //更新数据库中的附件上传状态
            fileUploadPieceRecordMapper.updateById(dbData);
            return dbData;
        } catch (Exception ex) {
            log.error("发生异常，异常信息为：{}", ex);
            return ResponseUtil.error("合并失败");
        }
    }

    @Override
    public Object authUpload(MultipartFile file, String fileName) {
        return this.upload(file, fileName, FileConstant.UPLOAD_TYPE_AUTH, MinIoUtils.originFileBucKet);
    }

    @Override
    public Object allowUpload(MultipartFile file, String fileName) {
        return this.upload(file, fileName, FileConstant.UPLOAD_TYPE_ALLOW, MinIoUtils.allowOriginFileBucKet);
    }

    @Override
    public Object getPicBase64(FileUploadRecord fileUploadRecord) {
        Map<String, String> result = new HashMap<>(16);
        try {
            byte[] file = new byte[0];
            if (!StrUtil.isEmpty(fileUploadRecord.getFilePath())) {
                file = MinIoUtils.getObject(fileUploadRecord.getFileBucketName(), fileUploadRecord.getFilePath());
            }
            String encoded = Base64.getEncoder().encodeToString(file);
            String type = "";
            if (fileUploadRecord.getFilePath().toLowerCase().contains(".jpg")) {
                type = "data:image/jpeg;base64,";
            } else if (fileUploadRecord.getFilePath().toLowerCase().contains(".png")) {
                type = "data:image/png;base64,";
            } else if (fileUploadRecord.getFilePath().toLowerCase().contains(".gif")) {
                type = "data:image/gif;base64,";
            }
            result.put("base64", type + encoded);
        } catch (Exception ex) {
            log.error("发生异常，异常信息为：{}", ex);
            return ResponseUtil.error("获取异常");
        }
        return result;
    }

    /**
     * 上传
     *
     * @param file
     * @param fileName
     * @param type
     * @param fileBucketName
     * @return
     */
    private Object upload(MultipartFile file, String fileName, String type, String fileBucketName) {
        try {
            String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
            String saveUrl = MinIoUtils.upload(file.getInputStream(), suffix, fileBucketName);
            FileUploadRecord fileUploadRecord = new FileUploadRecord();
            fileUploadRecord.setFileBucketName(fileBucketName);
            fileUploadRecord.setFileName(fileName);
            fileUploadRecord.setFileSuffix(suffix);
            fileUploadRecord.setFilePath(saveUrl);
            fileUploadRecord.setFileType(type);

            fileUploadRecord.setCreateTime(DateUtils.toLocalDateTime(new Date()));
            fileUploadRecord.setUpdateTime(DateUtils.toLocalDateTime(new Date()));
            fileUploadRecordMapper.insert(fileUploadRecord);
            return fileUploadRecord;
        } catch (Exception ex) {
            log.error("发生异常，异常信息为：{}", ex);
            return ResponseUtil.error("上传失败");
        }
    }

}
